#include "ByteStream.h"
#include <windows.h>
#include <mutex>




ByteStream::ByteStream(int BufferSize){
  this->Lock=new std::mutex();

  this->BufferSize=BufferSize;
  this->Buffer=new unsigned char[BufferSize];

  this->InEvent=CreateEvent(nullptr,false,false,nullptr);
  this->OutEvent=CreateEvent(nullptr,false,false,nullptr);

  this->I=0;
  this->O=0;
}

ByteStream::~ByteStream(){
  
  CloseHandle((HANDLE)InEvent);
  CloseHandle((HANDLE)OutEvent);

  delete (std::mutex*)Lock;

  delete[] Buffer;

}

void ByteStream::Write(unsigned char* Data,int Length){

  while(0!=TryWrite(Data,Length)){
    WaitForSingleObject((HANDLE)OutEvent,INFINITE);
  }
}

void ByteStream::Read(unsigned char* Data,int Length){

  while(0!=TryRead(Data,Length)){
    WaitForSingleObject((HANDLE)InEvent,INFINITE);
  }
}

int ByteStream::TryWrite(unsigned char* Data,int Length){
  std::mutex* mutex;
  int I;
  int EmptyLength;

  mutex=(std::mutex*)Lock;

  mutex->lock();

  I=this->I;
  const int O=this->O;

  if(I<O){
    EmptyLength=O-I;
  }else{
    EmptyLength=BufferSize-I+O;
  }

  if(EmptyLength>Length){

    EmptyLength=BufferSize-I;
    if(EmptyLength>Length){

      memcpy(&Buffer[I],Data,Length);

      this->I=I+Length;
    }else{

      memcpy(&Buffer[I],Data,EmptyLength);

      Data=&Data[EmptyLength];
      Length-=EmptyLength;
      
      if(Length>0){
        memcpy(&Buffer[0],Data,Length);
      }

      this->I=Length;
    }
    
    mutex->unlock();
    SetEvent((HANDLE)InEvent);
    return 0;
  }else{
    mutex->unlock();
    return -1;
  }

}

int ByteStream::TryRead(unsigned char* Data,int Length){
  std::mutex* mutex;
  int O;
  int UsedLength;

  mutex=(std::mutex*)Lock;

  mutex->lock();

  const int I=this->I;
  O=this->O;

  if(I<O){
    UsedLength=BufferSize+O-I;
  }else{
    UsedLength=I-O;
  }

  if(UsedLength<Length){
    mutex->unlock();
    return -1;
  }

  UsedLength=BufferSize-O;
  if(UsedLength>Length){

    memcpy(Data,&Buffer[O],Length);

    this->O=O+Length;
  }else{

    memcpy(Data,&Buffer[O],UsedLength);

    Data=&Data[UsedLength];
    Length-=UsedLength;
    
    if(Length>0){
      memcpy(Data,&Buffer[0],Length);
    }

    this->O=Length;
  }

  mutex->unlock();
  SetEvent((HANDLE)OutEvent);
  return 0;
}



